/* ****************************************************************** 
   File name: C_Log_Writer.h
   Associated Files necessary to complete this utility:
   C_Log_File_Writer.cpp
   C_TCP_Format_WSA_Text.h
   C_TCP_Format_WSA_Text.cpp


********************************************************************* */
/* ******************************************************************
    (C) Copyright 2012/2013 Bryan Kelly
    This file is part of the software utility named:
    Bryan Kelly's Utility Kit for Microsoft Windows.
    The short name is Bryan Kelly's Utility Kit.

    Bryan Kelly's Utility Kit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Bryan Kelly's Utility Kit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with BK TCP/IP Starter.  If not, see <http://www.gnu.org/licenses/>.

 
********************************************************************* */

/*  *****************************************************************

    Caution to the reader:  (And to myself)
    Regarding this line of code:
    errno_t copy_status = wcsncpy_s(  &build_string[ count_of_time_characters ], copy_count, caller_text_to_log, _TRUNCATE );

    At this writeing there are two Microsoft help pages:
    http://msdn.microsoft.com/en-us/library/5dae5d43.aspx
    http://msdn.microsoft.com/en-us/library/5dae5d43(VS.90).aspx

    The first references error code STRUNCATE.  The second was returned to me via the F1 help key
    and has no reference to STRUNCATE.

    The moral of the story:  When trying to understand a problem:  Don't stop at the first 
    Microsoft page that describes something.  Perform additional searches and read sources from
    other than Microsoft.

    ***************************************************************** */

#include "stdafx.h"
#include <iostream>
#include "Shlwapi.h"  // required for: PathFileExists()

#include "C_Log_Writer.h"  

using namespace std;


#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)

 
   // Constructor
C_Log_Writer::C_Log_Writer( const WCHAR *new_directory_name = &DEFAULT_DlRECTORY[0], 
                            const WCHAR *new_name_prefix    = &DEFAULT_FILENAME_PREFIX[0] )
{
   bool status              = true;
   m_log_writer_status      = true;
   m_file_output_enabled    = false;
   m_console_output_enabled = true;
   m_console_handle         = NULL;
   m_recursion_depth        = 0;
   m_auto_restart           = true;
   m_get_last_error         = 0;
   mp_log_FILE              = NULL;

   m_log_entry_count        = 0;


   wcscpy_s( m_directory_string, MAX_DIRECTORY_NAME_LENGTH, DEFAULT_DlRECTORY );
   wcscpy_s( m_prefix_string, MAX_FILE_NAME_PREFIX_LENGTH, DEFAULT_FILENAME_PREFIX );
   wcscpy_s( m_previous_prefix, MAX_FILE_NAME_PREFIX_LENGTH, DEFAULT_FILENAME_PREFIX );

   status = Set_Directory_String( new_directory_name );

   if( status )
   {
       status = Set_Prefix_String( new_name_prefix );
   }
 
   if( status )
   {
       status = Open_Log_File();
   }
 
}

/*  ***************************************************************************
Destructor
*********************************************************************** */
C_Log_Writer::~C_Log_Writer(void)
{
    Close_Log_File( DESTRUCTOR_IS_CLOSING );
    Close_Console_Window();
}


/* ***************************************************************************
Purpose: Provides the ability for the main application to provide a 
   specific prefix for easy identification of the log file.  Useful when
   multiple applications are active and write to log files.
   Saves the current prefix so it can be restored.
   Why?  Because I needed the ability to write a bunch of data to
      the log for a specific purpose, then resume normal operations.
      The extra/special data then has a unique prefix so it is easily identified.

      *********************************************************************** */
bool C_Log_Writer::Set_Prefix_String( const WCHAR * new_prefix_string )
{
    WCHAR local_string[ MAX_LOG_STRING_LENGTH ];

    if( new_prefix_string == NULL )
    {
        wcscpy_s( m_previous_prefix, MAX_FILE_NAME_PREFIX_LENGTH, m_prefix_string );
        wcscpy_s( m_prefix_string,   MAX_FILE_NAME_PREFIX_LENGTH, DEFAULT_FILENAME_PREFIX );

        swprintf_s( 
            local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Set_Prefix_String() received NULL prefix, using default: %s",
            m_prefix_string  );

        Write_Log_File_Entry( local_string );
        m_log_writer_status = false;
    }
    else
    {
        size_t new_length = wcsnlen_s( new_prefix_string, MAX_FILE_NAME_PREFIX_LENGTH );
        if( new_length >= MAX_FILE_NAME_PREFIX_LENGTH )
        {
            swprintf_s( local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Set_Prefix_String() received prefix of length %d, max allowed is %d.  Prefix not changed",
                new_length,
                MAX_FILE_NAME_PREFIX_LENGTH - 1 );
            Write_Log_File_Entry( local_string );

            m_log_writer_status = false;
        }
        else
        {
            wcscpy_s( m_previous_prefix, MAX_FILE_NAME_PREFIX_LENGTH, m_prefix_string );
            wcscpy_s( m_prefix_string,   MAX_FILE_NAME_PREFIX_LENGTH, new_prefix_string );

            swprintf_s( 
                local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Set_Prefix_String() Set directory string to: %s",
                m_prefix_string );
            Write_Log_File_Entry( local_string );

        }
        m_log_writer_status = true;
    }
    return m_log_writer_status;
}

/* ***************************************************************************
Purpose: Restores the previous prefix string.
   Provides the ability for the main application to set a temporary log file
   prefix.
*********************************************************************** */
void C_Log_Writer::Restore_Previous_Prefix( )
{
      wcscpy_s( m_prefix_string, MAX_FILE_NAME_PREFIX_LENGTH, m_previous_prefix );
}

/* ***************************************************************************
Same stuff as for the directory name

*********************************************************************** */
bool C_Log_Writer::Set_Directory_String( const WCHAR * new_directory_string )
{
    WCHAR local_string[           MAX_LOG_STRING_LENGTH ];
    WCHAR saved_directory_string[ MAX_DIRECTORY_NAME_LENGTH ];
 
    if( new_directory_string == NULL )
    {
        wcscpy_s( saved_directory_string, MAX_DIRECTORY_NAME_LENGTH, m_directory_string );
        wcscpy_s( m_directory_string, MAX_DIRECTORY_NAME_LENGTH, DEFAULT_DlRECTORY );

        swprintf_s( 
            local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Set_Directory_String() received NULL directory string, using default: %s",
            m_directory_string  );
        Write_Log_File_Entry( local_string );

        m_log_writer_status = true;
    }
    else
    {
        size_t input_length = wcsnlen_s( new_directory_string, MAX_PATH );
        if( input_length >= MAX_DIRECTORY_NAME_LENGTH )
        {
            swprintf_s( local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Set_Directory_String() supplied directory name too long "
                L"Found %d, allowed is %d. Directory not changed.",
                input_length,
                MAX_DIRECTORY_NAME_LENGTH );

            Write_Log_File_Entry( local_string );

        }
        else
        {
            wcscpy_s( saved_directory_string, MAX_DIRECTORY_NAME_LENGTH, m_directory_string );
            wcscpy_s( m_directory_string, MAX_DIRECTORY_NAME_LENGTH, new_directory_string );

            swprintf_s( local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Set_Directory_String() New directory string is %s ",
                m_directory_string );

            Write_Log_File_Entry( local_string );

            m_log_writer_status = true;
        }
    }

    if(   PathFileExistsW( m_directory_string ) == DIRECTORY_DOES_NOT_EXIST )
    {
        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Set_Directory_String(): PathFileExistsW(): reports directory does not exist."
            L" Restoring original directory string, new string \n\r %s",
            new_directory_string );

        // restore the current directory string.
        wcscpy_s( m_directory_string, MAX_DIRECTORY_NAME_LENGTH, saved_directory_string );
        m_log_writer_status = false;

        Write_Log_File_Entry( local_string );

    }
    return m_log_writer_status;
}

/*  ***************************************************************************
*********************************************************************** */
void C_Log_Writer::Set_Log_File_Write_Enable_Flag( bool new_value )
{
    if( new_value == true )
    {
        if( m_file_output_enabled == true )
            Write_Log_File_Entry( L"Enabled already enabled file logging." );
        else
        {
            m_file_output_enabled = true;
            Write_Log_File_Entry( L"Enabled file logging." );
        }
    }
    else
    {
        if( m_file_output_enabled == false )
            Write_Log_File_Entry( L"Disabled already disabled file logging." );
        else
            Write_Log_File_Entry( L"Disabling file logging." );

        m_file_output_enabled = false;
    }

}

/*  ***************************************************************************
*********************************************************************** */

bool C_Log_Writer::Get_Log_File_Write_Enable_Flag()
{
    return m_file_output_enabled;
}

/* ***************************************************************************
*********************************************************************** */

bool C_Log_Writer::Open_Log_File( )
{
   WCHAR text_to_log[ MAX_LOG_STRING_LENGTH ];
    
   if( PathFileExistsW( m_directory_string ) == DIRECTORY_DOES_NOT_EXIST )
   {

       swprintf_s( text_to_log,
           MAX_LOG_STRING_LENGTH,
           L" C_Log_Writer::Open_Log_File() Cannot open log file, PathFileExistsW() reports directory does not exist: %s",
           m_directory_string );

       Write_Log_File_Entry( text_to_log );
       // m_file_output_enabled  // do not change, if the file was already open, it can still be written to.
       return false;

   }

   Build_Log_File_Name();  // full name put here:  m_full_path_and_name

   errno_t open_status = _wfopen_s( 
                 &mp_log_FILE,
                 m_full_path_and_name, 
                 L"w, ccs=UNICODE" );

   if( open_status == ZERO_IS_SUCCESS )
   {
       m_file_output_enabled = true;

       Write_Log_File_Entry( OPEN_FILE_NOTICE );
   }
   else
   {
       m_file_output_enabled = false;

       // This is an error condition.  Cannot write to the file that cannot be opened.
       // But the console might be available.


       m_get_last_error = GetLastError();

       m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );

       size_t last_error_count = wcsnlen_s( m_text_get_last_error, MAX_WSA_ERROR_TEXT  );

       swprintf_s(  text_to_log, 
           MAX_LOG_STRING_LENGTH, 
           L"C_Log_Writer::Open_Log_File() Unable to open log file %s, error %d, %s",
           m_full_path_and_name,
           m_get_last_error,
           m_text_get_last_error  );

       Write_Log_File_Entry( text_to_log );

   }

   return m_file_output_enabled;
} // end of: Open_Log_File()

/*  ***************************************************************************
   Purpose: Break out the code to build the log file name.
   Used when opening a log file.
   Builds new name based on current time.
   The format is prefix then date and time because:
      When using Windows Explorer sort by date modified or sort by name as desired.

*********************************************************************** */

void C_Log_Writer::Build_Log_File_Name()
{
   GetSystemTime( &m_time_of_day);

     // format is: directory\prefix_YYYY_MM_DD_HH_MM_SS
   swprintf_s( m_full_path_and_name,
      MAX_PATH,
      L"%s\\%s_%04d_%02d_%03d-%03d-%02d-%02d.txt",  // 25
      m_directory_string,
      m_prefix_string,
      m_time_of_day.wYear,
      m_time_of_day.wMonth,
      m_time_of_day.wDay,
      m_time_of_day.wHour,
      m_time_of_day.wMinute,
      m_time_of_day.wSecond );

}  // end of: Build_Log_File_Name

/* ***************************************************************************
Owner can start another log file.
*********************************************************************** */

bool C_Log_Writer::Re_Open_Log_File()
{

    Write_Log_File_Entry( START_NEW_FILE_NOTICE );

    bool close_status = Close_Log_File();
    bool open_status = Open_Log_File();

    return close_status && open_status;
}


/*  ***************************************************************************
    Write to the log file. Preface each entry with the current time.
*********************************************************************** */

bool C_Log_Writer::Write_Log_File_Entry( const WCHAR *caller_text_to_log )
{
    DWORD debug_count_from_caller;
    DWORD debug_final_length;
    size_t copy_count = 0;
    DWORD count_from_caller = 0;

    if( mp_log_FILE == NULL || (unsigned int) mp_log_FILE == 0Xcdcdcdcd )
        m_file_output_enabled = false;


    if( !m_file_output_enabled &&  m_console_handle == NULL )
        return false;

    WCHAR temp_string[ MAX_LOG_STRING_LENGTH ];

 
    bool write_file_status    = true;
    bool write_console_status = true;

    //int  status_of_fputws = -1;

    DWORD count_of_time_characters        = 0;  // count of characters that will be written
    DWORD wrote_to_file_count    = 0;  // count written to log file
    DWORD wrote_to_console_count = 0;  // count written to console

    m_log_writer_status       = true;

    GetSystemTime( &m_time_of_day);

    WCHAR build_string[ MAX_INTERNAL_LOG_LENGTH ];
    count_of_time_characters = swprintf_s( 
        build_string,
        MAX_INTERNAL_LOG_LENGTH,
        L"\r\n%02d:%02d:%02d.%03d ",
        m_time_of_day.wHour,
        m_time_of_day.wMinute,
        m_time_of_day.wSecond,
        m_time_of_day.wMilliseconds );

    if( caller_text_to_log == NULL )
    {
        // do nothing, this will log the date and time only.
    }
    else
    {
            // check the length of the input string,  if it fits, concatenate on to the time
            // Why 1024 here.  Because we don't know how long the input string is until we count it.
            // If MAX_LOG_STRING_LENGTH (value = 400) is used there, the count routine stops counting
            // at 400 and we don't know that the string is too long.  So pick an arbitrary number
            // bigger than MAX_LOG_STRING_LENGTH.  The code below enforces the maximum lengt
            // copied to produce a legal output.
        count_from_caller = wcsnlen_s( caller_text_to_log, 1024 );
        debug_count_from_caller = count_from_caller;
        debug_final_length = MAX_INTERNAL_LOG_LENGTH;

        if( (count_from_caller + count_of_time_characters) < MAX_INTERNAL_LOG_LENGTH )
        {
            wcscat_s( build_string, MAX_INTERNAL_LOG_LENGTH, caller_text_to_log );
        }
        else  // it does not fit
        {
            // Copy what will fit into the write string
            copy_count = MAX_INTERNAL_LOG_LENGTH - ( count_of_time_characters + 1 );
            errno_t copy_status = wcsncpy_s(  &build_string[ count_of_time_characters ], copy_count, caller_text_to_log, _TRUNCATE );

            // Contrary to the F1 help pages, STRUNCATE is a good return value.
            // see include file  errno.h
            if( copy_status != 0 && copy_status != STRUNCATE )
            {

                if( m_recursion_depth < 1 )  // prevent recursion runaway
                {
                    int x = EINVAL;
                    x = ERANGE;
                    if( copy_status == EINVAL ) // EINVAL = 22 decimal, 16 hex
                    {
                        swprintf_s( 
                            temp_string,
                            MAX_LOG_STRING_LENGTH,
                            L"C_Log_Writer::Write_Log_File_Entry() reports copy error EINVAL: %d",
                            copy_status );
                    }
                    else if( copy_status == ERANGE ) // ERANGE = 34 decimal, 22 hex
                    {
                        swprintf_s( 
                            temp_string,
                            MAX_LOG_STRING_LENGTH,
                            L"C_Log_Writer::Write_Log_File_Entry() reports copy error ERANGE: %d",
                            copy_status );
                    }
                    else if( copy_status != ZERO_IS_SUCCESS )
                    {
                        swprintf_s( 
                            temp_string,
                            MAX_LOG_STRING_LENGTH,
                            L"C_Log_Writer::Write_Log_File_Entry() reports copy error: %d",
                            copy_status );
                    }

                    m_recursion_depth ++;
                    Write_Log_File_Entry( temp_string );
                    m_recursion_depth --;
                }
            }
        }
    }

    debug_final_length = wcsnlen_s( build_string, MAX_INTERNAL_LOG_LENGTH );

    if( m_file_output_enabled )
    {
        write_file_status = Write_To_File( build_string );
    }

    if( m_console_output_enabled )
    {
        write_console_status = Write_To_Console( build_string );
    }

    if( copy_count > 0 ) 
    {
           // Tell the caller something was wrong.
        write_file_status = false;
        swprintf_s( 
            temp_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Write_Log_File_Entry() reports previous entry too long. Received %d"
            L" from caller, only %d allowed, string truncated.",
            count_from_caller,
            MAX_LOG_STRING_LENGTH );
        Write_To_Console( temp_string );

    }

    return ( write_file_status && write_console_status );

} // end of: Write_Log_File_Entry( ...)

/*  ***********************************************************************
    Write to the file
    *********************************************************************** */

bool C_Log_Writer::Write_To_File( const WCHAR *data_to_log )
{
    if( !m_file_output_enabled || mp_log_FILE == NULL )
    {
        return true;
    }

    WCHAR text_to_log[ MAX_LOG_STRING_LENGTH ];
    int wrote_to_file_count = 0;
    int count_to_output     = 0;

    wrote_to_file_count = fputws( data_to_log, mp_log_FILE );

    if( wrote_to_file_count < ZERO_OR_GREATER_IS_SUCCESS )
    {
        m_log_writer_status = false;

        if( m_recursion_depth < 1 )
        {
        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );

           // bk: write some code to log this error
        size_t last_error_count = wcsnlen_s( m_text_get_last_error, MAX_WSA_ERROR_TEXT  );
        if( last_error_count >= MAX_WSA_ERROR_TEXT )
            m_text_get_last_error[ MAX_WSA_ERROR_TEXT ] = ( WCHAR ) 0;

        count_to_output = swprintf_s( 
            text_to_log,
            MAX_LOG_STRING_LENGTH,
            L"Error:       C_Log_Writer: Write_Log_File_Entry() returned %d, last error %d: %s",

            wrote_to_file_count,
            m_get_last_error,
            m_text_get_last_error );

        m_recursion_depth ++;
        Write_Log_File_Entry( text_to_log );
        m_recursion_depth --;

        }

    }
    else 
    {
        m_log_writer_status = true;
    } // else the write was ok.

    m_log_entry_count ++;
    if( m_log_entry_count > LOG_FILE_RESTART_COUNT &&
        m_auto_restart == true )
    {
        Re_Open_Log_File();
        m_log_entry_count = 0;
    }

    return m_log_writer_status;
} // end of: Write_To_File()


/*  ***********************************************************************
    Write to the console window
    *********************************************************************** */

bool C_Log_Writer::Write_To_Console( const WCHAR *data_to_log )
{

    BOOL   write_console_status  = FALSE;
    DWORD  count_to_output       = 0;
    DWORD wrote_to_console_count = 0;
    bool  method_status          = true;

    if( m_console_output_enabled == false ||
        m_console_handle         == NULL  ||
        m_console_handle         == INVALID_HANDLE_VALUE )
    {
        return method_status;
    }

    count_to_output = wcsnlen_s( data_to_log, MAX_LOG_STRING_LENGTH );  
    write_console_status = WriteConsole(
        m_console_handle,
        data_to_log,
        count_to_output,
        &wrote_to_console_count,
        NULL );

        // If there is an WriteConsole() error, try to log that information.
    if( write_console_status == WRITE_CONSOLE_ERROR &&
        m_recursion_depth < 1 )
    {
        method_status = false;
        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );

        size_t last_error_count = wcsnlen_s( m_text_get_last_error, MAX_WSA_ERROR_TEXT  );
        if( last_error_count >= MAX_WSA_ERROR_TEXT )
            m_text_get_last_error[ MAX_WSA_ERROR_TEXT ] = ( WCHAR ) 0;

        WCHAR error_text[ MAX_LOG_STRING_LENGTH ];
        swprintf_s( 
            error_text,
            MAX_LOG_STRING_LENGTH,
            L"Error:       C_Log_Writer: Write_To_Console() returned %d, last error %d: %s",

            wrote_to_console_count,
            m_get_last_error,
            m_text_get_last_error );

        m_recursion_depth ++;
        Write_Log_File_Entry( error_text );
        m_recursion_depth --;

    }
    return method_status;
} // end of: Write_To_Console()


/*  ***********************************************************************
    *********************************************************************** */

bool C_Log_Writer::Close_Log_File( bool who_is_closer  )
{
    bool method_status = false;
   if( mp_log_FILE != NULL)
   {
       if( m_recursion_depth < 1 )
       {
           m_recursion_depth ++;
           if( who_is_closer == APPLICATION_IS_CLOSING )
                Write_Log_File_Entry( &APPLICATION_CLOSE_FILE_NOTICE[0] );
           else
                Write_Log_File_Entry( &DESTRUCTOR_CLOSE_FILE_NOTICE[0] );

           m_recursion_depth --;
       }

      if( fclose( mp_log_FILE ) == ZERO_IS_SUCCESS )
          method_status = true;
      mp_log_FILE = NULL;
   }
   return method_status;
}
/*  ***************************************************************************
    Return true if the log file is open.
    I have seen pointers set to 0XCDCDCDCD several times, so check for that.
    ************************************************************************* */

bool C_Log_Writer::Is_Log_File_Open()
{
   if( mp_log_FILE != NULL                 &&
       mp_log_FILE != INVALID_HANDLE_VALUE &&
      (unsigned int) mp_log_FILE != 0XCDCDCDCD     )
      return true;
   else
       return false;
}

/*  ***************************************************************************

bk: test this to set the background color
Console.BackgroundColor = ConsoleColor.Red;

Console.Clear();

    *********************************************************************** */

bool C_Log_Writer::Open_Console_Window()
{
    bool method_status = true;
    WCHAR local_string[ MAX_LOG_STRING_LENGTH ];

    // If open, close and re-open.
    if( m_console_handle != NULL &&
        m_console_handle != INVALID_HANDLE_VALUE )
    {
        Write_Log_File_Entry( L"Close console Window for re-open." );
        if( Close_Console_Window() == true )
            m_console_output_enabled = false;
        else
            Write_Log_File_Entry( L"Error closing an open console window.  Open method continues." );
    }


    BOOL status = AllocConsole();
    if( status == ZERO_IS_ERROR )
    {
        m_console_output_enabled = false;
        method_status = false;
        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );

        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Open_Console_Window() open console failed,  error code %d, %s",
            m_get_last_error,
            m_text_get_last_error );
        Write_Log_File_Entry( local_string );
        return false;
    } 


    m_console_handle = GetStdHandle( STD_OUTPUT_HANDLE );

    if( m_console_handle == INVALID_HANDLE_VALUE )
    {
        method_status = false;
        m_console_output_enabled = false;

        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );
        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Open_Console_Window() GetStdHandle() failed,  error code %d, %s",
            m_get_last_error,
            m_text_get_last_error );
        Write_Log_File_Entry( local_string );
        return false;
    }

    // The above two sections contain code that can return fatal errors.
    // Now that this point has been achieved, we can write to the console.
    // It might not be what we want, wrong color maybe, but writes should be sucessful.

    m_console_output_enabled = true;

    // Very curious that this does not have a handle argugment but SetConsoleTextAttribute() does.
    BOOL call_status = SetConsoleTitleW( m_prefix_string );  // fail = 0

    // This could fail, but I presume the console could still be usable.
    // Note the error, but continue.
    if( call_status == ZERO_IS_ERROR )
    {
        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );
        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Open_Console_Window() SetConsoleTitle() failed,  error code %d, %s",
            m_get_last_error,
            m_text_get_last_error );
        Write_Log_File_Entry( local_string );
    }

    const WORD text_attributes = BACKGROUND_GREEN | BACKGROUND_RED | FOREGROUND_BLUE;
    call_status = SetConsoleTextAttribute( m_console_handle, text_attributes );

    if( call_status == ZERO_IS_ERROR )
    {
        m_get_last_error = GetLastError();
        m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( m_get_last_error, m_text_get_last_error );
        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Open_Console_Window() SetConsoleTextAttribute() failed,  error code %d, %s",
            m_get_last_error,
            m_text_get_last_error );
        Write_Log_File_Entry( local_string );
    }

    Write_Log_File_Entry( OPENED_CONSOLE_NOTICE  );



    return method_status;
} // end of: Open_Console_Window()

/*  ***************************************************************************
    *********************************************************************** */

bool C_Log_Writer::Close_Console_Window()
{
 
    WCHAR local_string[ MAX_LOG_STRING_LENGTH ];
    int  close_status = 0;
    bool module_status = true;

    if( m_console_handle == NULL ||
        m_console_handle == INVALID_HANDLE_VALUE )
    {
        if( m_recursion_depth < 1 )
        {
            m_recursion_depth ++;
            Write_Log_File_Entry( L"Attempted to close non-open Console Window" );
            m_recursion_depth --;
        }
        m_console_output_enabled = false;
        return true;
    }

    m_console_output_enabled = false;

    m_console_handle = NULL;
    close_status =FreeConsole();

    if( close_status == ZERO_IS_ERROR )
    {
        module_status = false;
        m_get_last_error = GetLastError();

        // There are no parameters so the use of this return value is improper.
        // Write out a valid error statement.
        if( m_get_last_error ==  ERROR_INVALID_PARAMETER  )
        {
            swprintf_s( local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Close_Console_Window() FreeConsole() reports console not attached." );
        }
        else
        {
            m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( 
                m_get_last_error, 
                m_text_get_last_error );


            swprintf_s( local_string,
                MAX_LOG_STRING_LENGTH,
                L"C_Log_Writer::Close_Console_Window() FreeConsole() returned non-zero value of %d, last_error is %d, %s ",
                close_status,
                m_get_last_error,
                m_text_get_last_error );
        }
    }
    else
    {
        swprintf_s( local_string,
            MAX_LOG_STRING_LENGTH,
            L"C_Log_Writer::Close_Console_Window() suceeded" );
    }

    if( m_recursion_depth < 1 )
    {
        m_recursion_depth ++;
        Write_Log_File_Entry( local_string );
        m_recursion_depth --;
    }

    return module_status;
}

bool C_Log_Writer::Is_Console_Window_Open()
{
 if( m_console_handle != NULL                 &&
       m_console_handle != INVALID_HANDLE_VALUE &&
      (unsigned int) m_console_handle != 0XCDCDCDCD     )
      return true;
   else
       return false;
}

/*  ***************************************************************************
    *********************************************************************** */
void C_Log_Writer::Set_Console_Output_Enable_Flag( bool new_value )
{
    if( new_value == true )
    {
        if( m_console_output_enabled == true )
            Write_Log_File_Entry( L"Enabling already enabled Console" );
        else
        {
            m_console_output_enabled = new_value;
            Write_Log_File_Entry( L"Enabling Console output" );
        }

        if( m_console_handle == NULL )
            Write_Log_File_Entry( L"Warning: Console output was enabled but console is not open." );
    }
    else  // new_value == false
    {
        Write_Log_File_Entry( L"Disabling console." );
        if( m_console_handle == NULL )
            Write_Log_File_Entry( L"Warning: Console output was disabled but console is not open." );

        if( m_console_output_enabled == false )
            Write_Log_File_Entry( L"Dis-abling already dis-abled Console" );

        m_console_output_enabled = false;
    }
}

/*  ***************************************************************************
    *********************************************************************** */

bool C_Log_Writer::Get_Console_Output_Enable_Flag( )
{
    return m_console_output_enabled;
}

/*  ***************************************************************************
    *********************************************************************** */

void C_Log_Writer::Set_Automatic_Restart_Flag( bool new_value )
{
    m_auto_restart = new_value;
}

/*  ***************************************************************************
    *********************************************************************** */

bool C_Log_Writer::Get_Automatic_Restart_Flag( )
{
    return m_auto_restart;
}




/*  *****************************************************************
    This was created becuase I wanted to show the status of multiple
    modules in the same format without burdening that code with the
    details.  If needed again, here it is.
*/

void C_Log_Writer::Show_Method_Status( const WCHAR *method_name, 
                                        WCHAR *optional_phrase, 
                                        bool method_status,
                                        int  wsa_error )
{


    const WCHAR METHOD_NAME[] = L"Show_Status()";
    WCHAR      log_string[ MAX_INTERNAL_LOG_LENGTH ] = { 0 };
    WCHAR      wsa_error_text[ MAX_WSA_ERROR_TEXT ] = { 0 };

  

    if( m_console_output_enabled || m_file_output_enabled )
    {

        if( wsa_error != 0 )
        {
            m_C_TCP_Format_WSA_Text.Format_WSA_Error_As_Text( wsa_error, wsa_error_text );
        }
        

        int wsa_length = wcsnlen_s( wsa_error_text, MAX_WSA_ERROR_TEXT );

        swprintf_s( log_string, 
            MAX_INTERNAL_LOG_LENGTH, 
            L"%s  status: %s %s wsa_error %d %s",
            method_name,
            method_status == true ? L"true " : L"false ",
            optional_phrase,
            wsa_error,
            (wsa_length > 1 )  ? wsa_error_text : L""  );

        if( m_console_output_enabled )
        {
//            cout << endl << log_string;
        }

        if( m_file_output_enabled  )
        {
            Write_Log_File_Entry( log_string );
        }
    }
} // end of: Show_Status()